package com.dsideal.cloud.cas.client.cas.restful;

import com.dsideal.cloud.auth.common.util.jwt.IJWTInfo;
import com.dsideal.cloud.auth.core.jwt.UserAuthenticationUtil;
import com.dsideal.cloud.cas.client.cache.RedisUserCache;
import com.dsideal.cloud.cas.client.cas.entity.CasUserDetails;
import com.dsideal.cloud.cas.client.cas.exception.InvalidTicketException;
import com.dsideal.cloud.cas.client.cas.properties.CasConfiguration;
import com.dsideal.cloud.cas.client.constant.CasConstants;
import com.dsideal.cloud.cas.client.utils.JwtTokenUtil;
import com.dsideal.cloud.common.log.DsLogger;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.stereotype.Service;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * CAS Restful API
 * 用户登录认证/获取TGT、ST工具类
 *
 * @author suny
 * @date 2018/11/27
 */
@Service
public class CasRestfulService {

    private final static DsLogger log = DsLogger.buildLogger(CasRestfulService.class);

    @Autowired
    private CasConfiguration casConfiguration;

    @Autowired
    private RedisUserCache redisUserCache;


    @Autowired
    private UserAuthenticationUtil userAuthenticationUtil;
    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    /**
     * 获取重定向URL
     *
     * @param params 自定义参数，包含username,password,code等等
     * @return redirectUrl 自定义的重定向地址
     */
    public String authenticate(Map<String, String> params) {
        String redirectUrl = null;
        String tgt = getTGT(params);
        if (StringUtils.isNotBlank(tgt)) {
            String st = getST(tgt);
            if (StringUtils.isNotBlank(st) && st.startsWith("ST-")) {
                redirectUrl = casConfiguration.getAppServerUrl() + casConfiguration.getAppLoginUrl()
                        + "?ticket=" + st;
                String username = params.get(CasConstants.USERNAME_KEY);
                String jwt = getJwtByUsername(username);
                if (jwt != null) {

                    CasUserDetails casUserDetails = new CasUserDetails();
                    casUserDetails.setUsername(username);
                    casUserDetails.setToken(jwt);
                }
            } else {
                return "";
            }
        } else {
           return "";
        }

        return redirectUrl;
    }

    /**
     * 登录并且返回用户信息
     *
     * @param params 自定义参数，包含username,password,code等等
     * @return redirectUrl 自定义的重定向地址
     */
    public CasUserDetails remoteLogin(Map<String, String> params) throws Exception {
        CasUserDetails casUserDetails = null;
        String tgt = getTGT(params);
        if (StringUtils.isNotBlank(tgt)) {
            String st = getST(tgt);
            if (StringUtils.isNotBlank(st) && st.startsWith("ST-")) {
                String username = params.get(CasConstants.USERNAME_KEY);
                String jwt = getJwtByUsername(username);
                if (jwt != null) {
                    casUserDetails = getUserFromToken(jwt);
                    casUserDetails.setToken(jwt);
                }
            } else {
                throw new InvalidTicketException("服务票据获取失败，请重试！");
            }
        } else {
            throw new BadCredentialsException("用户认证失败，请检查用户名密码！");
        }

        return casUserDetails;
    }

    /**
     * 根据用户名获取jwt
     *
     * @param username
     * @return
     */
    public String getJwtByUsername(String username) {
        return redisUserCache.getTokenFromCache(username);
    }

    /**
     * 解析token中的用户信息
     *
     * @param jwt
     * @return
     */
    public CasUserDetails getUserFromToken(String jwt) throws Exception {
        IJWTInfo jwtInfo = userAuthenticationUtil.getInfoFromToken(jwt);
        return CasUserDetails.fromJWTInfo(jwtInfo);
    }

    /**
     * 获取TGT
     *
     * @param params 根据params中参数拼装请求参数，username、password和code作为独立参数，
     *               其他自定义参数作为attributes参数
     * @return TGT
     */
    public String getTGT(Map<String, String> params) {
        String location = null;
        try {
            CloseableHttpClient client = HttpClients.createDefault();

            HttpPost httpPost = new HttpPost(casConfiguration.getTgtUrl());
            List<NameValuePair> plist = new ArrayList<>();

            Iterator<String> iterator = params.keySet().iterator();
            while (iterator.hasNext()) {
                String key = iterator.next();
                switch (key) {
                    case CasConstants.INDEX_URL_KEY:
                        break;
                    case CasConstants.USERNAME_KEY:
                        plist.add(new BasicNameValuePair(CasConstants.USERNAME_KEY, params.get(key)));
                        break;
                    case CasConstants.PASSWORD_KEY:
                        plist.add(new BasicNameValuePair(CasConstants.PASSWORD_KEY, params.get(key)));
                        break;
                    case CasConstants.SYSTEM_CODE_KEY:
                        plist.add(new BasicNameValuePair(CasConstants.SYSTEM_CODE_KEY, params.get(key)));
                        break;
                    default:
                        plist.add(new BasicNameValuePair(CasConstants.ATTRIBUTES_KEY + "[\"" + key + "\"]",
                                params.get(key)));
                }
            }
            httpPost.setEntity(new UrlEncodedFormEntity(plist));
            HttpResponse response = client.execute(httpPost);

            Header headerLocation = response.getFirstHeader("Location");
            location = headerLocation == null ? null : headerLocation.getValue();

            if (StringUtils.isNotBlank(location)) {
                location = location.substring(location.lastIndexOf("/") + 1);
            }
        } catch (Exception e) {
            log.error("getTGT error {}", e);
        }

        return location;
    }

    /**
     * 获取ST
     *
     * @param TGT
     * @return ST
     */
    public String getST(String TGT) {
        String st = null;
        try {
            CloseableHttpClient client = HttpClients.createDefault();

            HttpPost httpPost = new HttpPost(casConfiguration.getStUrl() + TGT);
            List<NameValuePair> params = new ArrayList<>();
            params.add(new BasicNameValuePair("service",
                    casConfiguration.getAppServerUrl() + casConfiguration.getAppLoginUrl()));
            httpPost.setEntity(new UrlEncodedFormEntity(params));
            HttpResponse response = client.execute(httpPost);

            st = readResponse(response);
        } catch (Exception e) {
            log.error("getST error {}", e);
        }

        return st;
    }

    /**
     * 读取 response body 内容为字符串
     *
     * @param response
     * @return
     * @throws IOException
     */
    private String readResponse(HttpResponse response) throws IOException {
        BufferedReader in = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
        String result = "";
        String line;
        while ((line = in.readLine()) != null) {
            result += line;
        }

        return result;
    }

}
